{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 参数化量子线路\n",
    "\n",
    "`Linux` `CPU` `全流程` `初级` `中级` `高级`\n",
    "\n",
    "[![](https://gitee.com/mindspore/docs/raw/master/tutorials/training/source_zh_cn/_static/logo_source.png)](https://gitee.com/mindspore/docs/blob/master/tutorials/training/source_zh_cn/advanced_use/parameterized_quantum_circuit.ipynb)\n",
    "\n",
    "## 概述\n",
    "\n",
    "参数化量子线路（Parameterized quantum circuit, PQC）是进行量子机器学习的一种途径，量子-经典混合机器学习架构MindQuantum能够处理带参数的量子线路，并利用量子神经网络的可逆性来对线路进行自动微分，得到观测值对各参数的导数。\n",
    "\n",
    "构建参数化量子线路并用参数化模拟器算子进行线路演化的大致流程如下：\n",
    "\n",
    "1. 初始化量子线路。\n",
    "2. 根据需求，在量子线路中加入参数化的量子门或者非参数的量子门。\n",
    "3. 利用PQC模拟器算子来进行态演化或者梯度求解。\n",
    "\n",
    "## 环境准备\n",
    "\n",
    "导入本教程所依赖模块。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import mindquantum as mq\n",
    "from mindquantum.gate import H, X, Y, RY, RX"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 量子门\n",
    "\n",
    "量子门是对量子比特进行操作的基本逻辑单元。对于经典电路来说，任意的逻辑电路都可以由一些基本逻辑门构成，类似的，任意的量子线路都可以由一些基本的量子门构成，如作用在单比特上的门和受控非门。常用的量子门有$\\text{X}$门、$\\text{Y}$门、$\\text{Z}$门、$\\text{Hadamard}$门、$\\text{CNOT}$门以及一些旋转门。例如，$\\text{Y}$门的形式如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Gate name:  Y\n",
      "Gate matrix: \n",
      " [[ 0.+0.j -0.-1.j]\n",
      " [ 0.+1.j  0.+0.j]]\n"
     ]
    }
   ],
   "source": [
    "print('Gate name: ', Y)\n",
    "print('Gate matrix: \\n', Y.matrix())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的$\\text{Z}$门是一个非参数门，而一些旋转门（比如$\\text{RY}$门）则是含参数门，通过给予不同的旋转角度$\\theta$，旋转门将对量子比特产生不同的影响，如$\\text{RY}$门矩阵的表达式为：\n",
    "\n",
    "$$\\text{RY}(\\theta)=e^{-i\\theta Y/2}=\\begin{pmatrix}\\cos(\\theta/2)&-\\sin(\\theta/2)\\\\\\sin(\\theta/2)&\\cos(\\theta/2)\\end{pmatrix}$$\n",
    "\n",
    "其中$i$为虚数基本单位。这种含参数的量子门是后续搭建量子神经网络的重要组成单元。下面，我们打印$\\text{RY}$门在旋转角度为$0.5$时的矩阵形式。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.96891242, -0.24740396],\n",
       "       [ 0.24740396,  0.96891242]])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ry = RY('a')\n",
    "ry.matrix({'a': 0.5})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 量子线路\n",
    "\n",
    "量子线路是用来对各种量子逻辑门进行有效组织的结构，我们可以通过量子门的list来初始化量子线路，也可以通过加法（`+`）加一个量子门或者线路，乘法（`*`）乘一个整数来扩充量子线路。这里我们来构建如下的量子线路，并打印量子线路的相关信息。下图中`q0`、`q1`和`q2`分别表示三个量子比特，量子线路由三个量子门构成，分别为作用在`q0`比特上的Hadamard门，作用在`q1`比特并受`q0`比特控制的$CNOT$门和作用在`q2`比特上的$\\text{RY}$旋转门。\n",
    "\n",
    "![quantum circuit](https://gitee.com/mindspore/docs/raw/master/tutorials/training/source_zh_cn/advanced_use/images/quantum_circuit.png)\n",
    "\n",
    "### [HiQsimulator](https://hiq.huaweicloud.com/doc/index.html) 兼容的量子线路搭建格式\n",
    "\n",
    "1. 使用`CircuitEngine`线路引擎来搭建量子线路\n",
    "\n",
    "    我们通过操作符“|”，将量子门作用在相应的量子比特上。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "H(0)\n",
      "X(1 <-: 0)\n",
      "RY(p1|2)\n",
      "========Circuit Summary========\n",
      "|Total number of gates  : 3.  |\n",
      "|Parameter gates        : 1.  |\n",
      "|with 1 parameters are  : p1. |\n",
      "|Number qubit of circuit: 3   |\n",
      "===============================\n"
     ]
    }
   ],
   "source": [
    "eng = mq.engine.CircuitEngine()\n",
    "qubits = eng.allocate_qureg(3)\n",
    "H | qubits[0]\n",
    "X | (qubits[0], qubits[1])\n",
    "RY('p1') | qubits[2]\n",
    "encoder = eng.circuit\n",
    "print(encoder)\n",
    "encoder.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "   这里`X(1 <-: 0)`表示受0比特控制、作用在1比特上的`X`门，也即CNOT门。`RY(p1|2)`表示作用在2比特上的绕Y轴旋转门，`p1`为旋转角度。通过打印出的Summary信息我们可以发现，该量子线路由三个量子门构成，其中有一个量子门是参数化量子门，整个量子线路用到的量子比特为3个。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. 使用装饰器来搭建量子线路\n",
    "\n",
    "    通过装饰器来搭建量子线路能够省去一些重复的引擎声明步骤。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "H(0)\n",
      "X(1 <-: 0)\n",
      "RY(p1|2)\n",
      "========Circuit Summary========\n",
      "|Total number of gates  : 3.  |\n",
      "|Parameter gates        : 1.  |\n",
      "|with 1 parameters are  : p1. |\n",
      "|Number qubit of circuit: 3   |\n",
      "===============================\n"
     ]
    }
   ],
   "source": [
    "from mindquantum.engine import circuit_generator\n",
    "\n",
    "@circuit_generator(3)\n",
    "def encoder(qubits):\n",
    "    H | qubits[0]\n",
    "    X | (qubits[0], qubits[1])\n",
    "    RY('p1') | qubits[2]\n",
    "\n",
    "print(encoder)\n",
    "encoder.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "   我们还可以给装饰器传入更多的参数来供线路生成时使用。例如可以传入一个字符串，搭建量子线路时，可以利用该字符串来给每个参数添加一个前缀，这样有利于生成结构相同，但参数名不同的量子线路。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "H(0)\n",
      "X(1 <-: 0)\n",
      "RY(encoder_1|2)\n",
      "===========Circuit Summary===========\n",
      "|Total number of gates  : 3.        |\n",
      "|Parameter gates        : 1.        |\n",
      "|with 1 parameters are  : encoder_1.|\n",
      "|Number qubit of circuit: 3         |\n",
      "=====================================\n"
     ]
    }
   ],
   "source": [
    "@circuit_generator(3, prefix='encoder')\n",
    "def encoder(qubits, prefix):\n",
    "    H | qubits[0]\n",
    "    X | (qubits[0], qubits[1])\n",
    "    RY(prefix + '_1') | qubits[2]\n",
    "\n",
    "print(encoder)\n",
    "encoder.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 更便捷的线路生成方式\n",
    "\n",
    "通过往量子线路中不断地添加作用在不同比特上的量子门可快速完成量子线路的搭建。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "H(0)\n",
      "X(1 <-: 0)\n",
      "RY(p1|2)\n",
      "========Circuit Summary========\n",
      "|Total number of gates  : 3.  |\n",
      "|Parameter gates        : 1.  |\n",
      "|with 1 parameters are  : p1. |\n",
      "|Number qubit of circuit: 3   |\n",
      "===============================\n"
     ]
    }
   ],
   "source": [
    "from mindquantum import Circuit\n",
    "\n",
    "encoder = Circuit()\n",
    "encoder += H.on(0)\n",
    "encoder += X.on(1,0)\n",
    "encoder += RY('p1').on(2)\n",
    "print(encoder)\n",
    "encoder.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 利用 MindSpore 算子进行量子线路的模拟"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一个常见的量子神经网络通常由如下三个部分构成：\n",
    "\n",
    "- 一个（或多个）编码线路，用于将经典数据编码为量子数据\n",
    "- 一个（或多个）待训练线路（通常称为Ansatz）\n",
    "- 一个（或多个）待测量物理量\n",
    "\n",
    "下面我们搭建如下的量子神经网络，该量子神经网络的编码部分由两个$\\text{RY}$门构成，而Ansatz线路由一个$\\text{CNOT}$门和两个$\\text{RX}$门构成，待测量物理量是作用在1号比特上的$\\text{Z}$算符。\n",
    "\n",
    "![simple qnn](https://gitee.com/mindspore/docs/raw/master/tutorials/training/source_zh_cn/advanced_use/images/simple_qnn.png)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "from projectq.ops import QubitOperator\n",
    "\n",
    "@circuit_generator(2)\n",
    "def encoder(qubits):\n",
    "    RY('a') | qubits[0]\n",
    "    RY('b') | qubits[1]\n",
    "\n",
    "@circuit_generator(2)\n",
    "def ansatz(qubits):\n",
    "    X | (qubits[0],qubits[1])\n",
    "    RX('p1') | qubits[0]\n",
    "    RX('p2') | qubits[1]\n",
    "\n",
    "ham = mq.Hamiltonian(QubitOperator('Z1'))\n",
    "encoder_names = ['a', 'b']\n",
    "ansatz_names = ['p1', 'p2']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里我们通过装饰器的方式生成了Encoder线路和Ansatz线路。并利用`generate_pqc_operator`方法来产生一个线路模拟算子，对该量子线路进行模拟计算，并求取量子神经网络的输出对各参数的梯度值。在`generate_pqc_operator`方法中，我们需要提供Encoder线路的参数名、Ansatz线路的参数名、整个量子线路和待测量的物理量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Measurement result:  [[0.89819133]]\n",
      "Gradient of encoder parameters:  [[[-0.09011973 -0.1820724 ]]]\n",
      "Gradient of ansatz parameters:  [[[-2.7755576e-17 -3.7974921e-01]]]\n"
     ]
    }
   ],
   "source": [
    "from mindquantum.nn import generate_pqc_operator\n",
    "from mindspore import Tensor\n",
    "from mindspore import context\n",
    "context.set_context(mode=context.GRAPH_MODE, device_target=\"CPU\")\n",
    "\n",
    "pqc = generate_pqc_operator(encoder_names, ansatz_names, encoder+ansatz, ham)\n",
    "encoder_data = Tensor(np.array([[0.1,0.2]]).astype(np.float32))\n",
    "ansatz_data = Tensor(np.array([0.3,0.4]).astype(np.float32))\n",
    "measure_result, encoder_grad, ansatz_grad = pqc(encoder_data, ansatz_data)\n",
    "print('Measurement result: ', measure_result.asnumpy())\n",
    "print('Gradient of encoder parameters: ', encoder_grad.asnumpy())\n",
    "print('Gradient of ansatz parameters: ', ansatz_grad.asnumpy())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的三个结果分别表示量子神经网络的输出值、编码线路中参数的梯度值和带训练Ansatz线路中参数的梯度值。有的时候，量子神经网络是作为整个量子经典混合神经网络的第一层，因此我们不用对编码线路中的梯度求导数，对于这种不需要求梯度的线路，可以通过`no_grad`方法指定不需要计算梯度的量子线路不求导。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Measurement result:  [[0.89819133]]\n",
      "Gradient of encoder parameters:  [[[0. 0.]]]\n",
      "Gradient of ansatz parameters:  [[[-2.7755576e-17 -3.7974921e-01]]]\n"
     ]
    }
   ],
   "source": [
    "encoder.no_grad()\n",
    "pqc = generate_pqc_operator(encoder_names, ansatz_names, encoder+ansatz, ham)\n",
    "measure_result, encoder_grad, ansatz_grad = pqc(encoder_data, ansatz_data)\n",
    "print('Measurement result: ', measure_result.asnumpy())\n",
    "print('Gradient of encoder parameters: ', encoder_grad.asnumpy())\n",
    "print('Gradient of ansatz parameters: ', ansatz_grad.asnumpy())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如上可知，量子神经网络中的编码线路参数的导数都为零，实际模拟计算过程中没有对其求导。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}